package top.chmin.project.cloud.builder;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import top.chmin.project.cloud.common.dao.BaseDao;
import top.chmin.project.cloud.common.entity.*;
import top.chmin.project.cloud.common.entity.auto.Entity;
import top.chmin.project.cloud.common.entity.auto.EntityDao;
import top.chmin.project.cloud.common.entity.auto.EntityFPC;
import top.chmin.project.cloud.common.entity.column.auto.EntityColumn;
import top.chmin.project.cloud.common.entity.column.auto.EntityColumnDao;
import top.chmin.project.cloud.common.entity.column.auto.EntityColumnFPC;
import top.chmin.project.cloud.common.entity.label.value.auto.EntityLabelValue;
import top.chmin.project.cloud.common.entity.label.value.auto.EntityLabelValueDao;
import top.chmin.project.cloud.common.entity.label.value.auto.EntityLabelValueFPC;
import top.chmin.project.cloud.common.tool.DatetimeTool;
import top.chmin.project.cloud.common.tool.LocalTool;

import java.io.File;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * chmin
 * 2023/12/15 15:21
 * cloud-builder
 */
public interface Builder {

    Logger log = LoggerFactory.getLogger(Builder.class);
    Map<String, Entity> entityInfoMap = new HashMap<>();
    Map<String, EntityColumn> fieldInfoMap = new HashMap<>();
    Map<String, List<EntityLabelValue>> labelValueMap = new HashMap<>();

    void build(BuilderConfig config);

    default Entity getEntity(EntityDao entityInfoDao, String tableCode) {
        if (entityInfoMap.containsKey(tableCode)) {
            return entityInfoMap.get(tableCode);
        }
        Entity entity = entityInfoDao.getFirst(
                new EntityFPC()
                        .tableCode().eq(tableCode)
                        .toCondition()
        );
        entityInfoMap.put(tableCode, entity);
        return entity;
    }

    default EntityColumn getEntityColumn(EntityColumnDao entityColumnDao, String fieldCode) {
        if (fieldInfoMap.containsKey(fieldCode)) {
            return fieldInfoMap.get(fieldCode);
        }
        EntityColumn entityColumn = entityColumnDao.getFirst(
                new EntityColumnFPC()
                        .fieldCode().eq(fieldCode)
                        .toCondition()
        );
        fieldInfoMap.put(fieldCode, entityColumn);
        return entityColumn;
    }

    default List<EntityLabelValue> getEntityLabelValue(EntityLabelValueDao entityLabelValueDao, String tableCode) {
        if (labelValueMap.containsKey(tableCode)) {
            return labelValueMap.get(tableCode);
        }
        List<EntityLabelValue> entityLabelValue = entityLabelValueDao.getStreamByCondition(
                new EntityLabelValueFPC()
                        .tableCode().eq(tableCode)
                        .toCondition()
        ).toList();
        labelValueMap.put(tableCode, entityLabelValue);
        return entityLabelValue;
    }

    default String fixEntityColumn(EntityColumn entityColumn, String content) {
        return content
//                .replace("#fN#", fieldInfo.getFieldName())
                .replace("#f.fieldName#", entityColumn.getFieldName())

//                .replace("#FN#", StringUtils.capitalize(fieldInfo.getFieldName()))
                .replace("#f.methodName#", entityColumn.getMethodName())

//                .replace("#f_n#", StrUtil.toUnderlineCase(fieldInfo.getFieldName()))
                .replace("#f.columnName#", entityColumn.getColumnName())

//                .replace("#fT#", fieldInfo.getType())
                .replace("#f.simpleType#", entityColumn.getSimpleType())
                .replace("#f.fullType#", entityColumn.getFullType())

//                .replace("#f.remark#", fieldInfo.getRemark())
                .replace("#f.remark#", entityColumn.getRemark());
    }


    default String fixContent(Entity entity, String content) {
        return content
                .replace("#dateTime#", DateUtil.formatLocalDateTime(LocalDateTime.now()))

//                .replace("#m#", entityInfo.getModuleName())
                .replace("#e.module#", entity.getModule())

//                .replace("#c.n#", StrUtil.toUnderlineCase(entityInfo.getClassName()).replace("_", "."))
                .replace("#e.pack#", entity.getPack())

//                .replace("#CN#", entityInfo.getClassName())
                .replace("#e.simpleName#", entity.getClassName())

//                .replace("#cN#", StringUtils.uncapitalize(entityInfo.getClassName()))
                .replace("#e.fieldName#", StringUtils.uncapitalize(entity.getClassName()))

//                .replace("#c_n#", StrUtil.toUnderlineCase(entityInfo.getClassName()))
                .replace("#e.tableName#", entity.getTableName())

                .replace("#import#", getImport(content))

//                .replace("#cR#", entityInfo.getRemark())
                .replace("#e.remark#", entity.getRemark());
    }

    default String getImport(String content) {
        StringBuilder sb = new StringBuilder();
        check(sb, content, List.class);
        check(sb, content, DatetimeTool.class);
        check(sb, content, BigDecimal.class);
        check(sb, content, LocalDateTime.class);
        check(sb, content, Stream.class);
        check(sb, content, Setter.class);
        check(sb, content, Getter.class);
        check(sb, content, Accessors.class);
        check(sb, content, Model.class);
        check(sb, content, Resource.class);
        check(sb, content, RestController.class);
        check(sb, content, Page.class);
        check(sb, content, Page.class);
        check(sb, content, Repository.class);
        check(sb, content, BaseDao.class);
        check(sb, content, BaseDao.class);
        check(sb, content, Condition.class);
        check(sb, content, Arrays.class);
        check(sb, content, PostMapping.class);
        check(sb, content, RequestBody.class);
        check(sb, content, PreCondition.class);
        check(sb, content, Collectors.class);
        check(sb, content, JSONUtil.class);
        check(sb, content, Slf4j.class);
        check(sb, content, Component.class);
        check(sb, content, LocalTool.class);
        check(sb, content, BeanUtil.class);
        check(sb, content, Service.class);
        check(sb, content, Transactional.class);
        check(sb, content, Map.class);
        check(sb, content, BigDecimalEPCField.class);
        check(sb, content, BigDecimalFPCField.class);
        check(sb, content, IntegerEPCField.class);
        check(sb, content, IntegerFPCField.class);
        check(sb, content, LocalDateTimeEPCField.class);
        check(sb, content, LocalDateTimeFPCField.class);
        check(sb, content, LongEPCField.class);
        check(sb, content, LongFPCField.class);
        check(sb, content, StringEPCField.class);
        check(sb, content, StringFPCField.class);
        check(sb, content, CollectionUtil.class);
        return sb.toString();
    }

    default void check(StringBuilder sb, String content, Class<?> clazz) {
        check(sb, content, clazz, " ", " ");
        check(sb, content, clazz, "(", " ");
        check(sb, content, clazz, " ", ".");
        check(sb, content, clazz, "(", ".");
        check(sb, content, clazz, " ", "<");
        check(sb, content, clazz, "(", "<");
        check(sb, content, clazz, "@", "");
    }

    default void check(StringBuilder sb, String content, Class<?> clazz, String prefix, String suffix) {
        if (content.contains(prefix + clazz.getSimpleName() + suffix) && !content.contains("import " + clazz.getName() + ";")) {
            sb.append("\nimport ").append(clazz.getName()).append(";");
        }
    }

    default void save(BuilderConfig config, String fileName, String content) {
        save(config, fileName, content, true);
    }

    default void save(BuilderConfig config, String fileName, String content, boolean override) {
        File file = new File("%s\\src\\main\\java\\top\\chmin\\project\\cloud\\%s\\%s\\auto\\%s.java".formatted(
                config.getConfig().getJavaPath(),
                config.getEntity().getModule(),
                StrUtil.toUnderlineCase(config.getEntity().getClassName()).replace("_", "\\"),
                fileName
        ));

        log.info("保存文件：{}", file.getAbsolutePath());

        content = fixContent(config.getEntity(), content);

        if (file.exists() && !override) {
            log.info("文件已存在，忽略生成");
        } else {
            FileUtil.writeString(
                    content,
                    file,
                    StandardCharsets.UTF_8
            );
        }
    }

    default void appendJava(BuilderConfig config, EntityColumn entityColumn, String position, String keyword, String content, String fileName) {
        File file = new File("%s\\src\\main\\java\\top\\chmin\\project\\cloud\\%s\\%s\\auto\\%s.java".formatted(
                config.getConfig().getJavaPath(),
                config.getEntity().getModule(),
                StrUtil.toUnderlineCase(config.getEntity().getClassName()).replace("_", "\\"),
                fileName
        ));
        content = fixContent(config.getEntity(), content);

        String source = FileUtil.readString(file, StandardCharsets.UTF_8);
        keyword = fixEntityColumn(entityColumn, keyword);
        if (source.contains(keyword)) {
            return;
        } else {
            source = source.replace(position, fixEntityColumn(entityColumn, """
                    %s
                    // #bind.field.#f.fieldName##
                    %s
                    // $bind.field.#f.fieldName#$
                    """.formatted(position, content)));
        }

        FileUtil.writeString(
                source,
                file,
                StandardCharsets.UTF_8
        );
    }

    default void appendJava(BuilderConfig config, String position, String keyword, String content, String fileName) {
        File file = new File("%s\\src\\main\\java\\top\\chmin\\project\\cloud\\%s\\%s\\auto\\%s.java".formatted(
                config.getConfig().getJavaPath(),
                config.getEntity().getModule(),
                StrUtil.toUnderlineCase(config.getEntity().getClassName()).replace("_", "\\"),
                fileName
        ));
        content = fixContent(config.getEntity(), content);

        String source = FileUtil.readString(file, StandardCharsets.UTF_8);
        if (source.contains(keyword)) {
            return;
        } else {
            source = source.replace(position, """
                    %s
                    %s
                    """.formatted(position, content));
        }

        FileUtil.writeString(
                source,
                file,
                StandardCharsets.UTF_8
        );
    }

    default void saveResource(BuilderConfig config, String fileName, String content) {
        saveResource(config, fileName, content, true);
    }

    default void saveResource(BuilderConfig config, String fileName, String content, boolean override) {
        File file = new File("%s\\src\\main\\resources\\sql\\%s".formatted(
                config.getConfig().getJavaPath(),
                fileName
        ));

        log.info("保存文件：{}", file.getAbsolutePath());

        if (file.exists() && !override) {
            log.info("文件已存在，忽略生成");
            return;
        }

        FileUtil.writeString(
                content,
                file,
                "UTF-8"
        );
    }

    default void saveUi(BuilderConfig config, String path, String content) {
        saveUi(config, path, content, true);
    }

    default void saveUi(BuilderConfig config, String path, String content, boolean override) {
        File file = new File("%s\\src\\%s".formatted(
                config.getConfig().getUiPath(),
                path
        ));

        log.info("保存文件：{}", file.getAbsolutePath());

        if (file.exists() && !override) {

            log.info("文件已存在，忽略生成");
            return;
        }

        content = fixContent(config.getEntity(), content);

        FileUtil.writeString(
                content,
                file,
                "UTF-8"
        );
    }


    default void appendUi(BuilderConfig config, EntityColumn entityColumn, String position, String keyword, String content, String path) {
        File file = new File("%s\\src\\%s".formatted(
                config.getConfig().getUiPath(),
                path
        ));
        content = fixContent(config.getEntity(), content);

        String source = FileUtil.readString(file, StandardCharsets.UTF_8);
        keyword = fixEntityColumn(entityColumn, keyword);
        if (source.contains(keyword)) {
            return;
        } else {
            source = source.replace(position, fixEntityColumn(entityColumn, """
                    %s
                    // #bind.field.#f.fieldName##
                    %s
                    // $bind.field.#f.fieldName#$
                    """.formatted(position, content)));
        }

        FileUtil.writeString(
                source,
                file,
                StandardCharsets.UTF_8
        );
    }

    default void replaceUi(BuilderConfig config, String keyword, String content, String path) {
        File file = new File("%s\\src\\%s".formatted(
                config.getConfig().getUiPath(),
                path
        ));
        content = fixContent(config.getEntity(), content);

        String source = FileUtil.readString(file, StandardCharsets.UTF_8);
        if (!source.contains(keyword)) {
            return;
        } else {
            source = source.replace(keyword, content);
        }

        FileUtil.writeString(
                source,
                file,
                StandardCharsets.UTF_8
        );
    }
}
